package intopo.game;

import java.io.PrintStream;

/**
 * This interface represents a game. Implementors of this interface should
 * specify how a game is played with some game players. A strategy of this
 * game is defined by classes (represented by generic {@code S}) that
 * implement interface {@code Strategy}
 *
 * <p> This interface also defines a method that returns the number of
 * players in a game. This is required in order to create a set of game
 * players, from a population of players.
 *
 * <p> There are two other methods that return the minimum and maximum
 * payoff attainable in a game.  These methods can be used to gather
 * statistic information.
 *
 * @see Strategy
 *
 * @param <S> The type of strategies to play this game.
 *
 * @author <a href="mailto:mariano.pedro@gmail.com">Pedro Lopes da Silva Mariano</a>

 * @version 2.0
 */
public interface Game<S extends Strategy<S>>
{
	/**
	 * Returns the number of players this game has.
	 *
	 * @return The number of players this game has.
	 */
	public int numberPlayers ();

	/**
	 * This method is responsible for simulating a game among the
	 * specified game players.  The parameter represents a strategy profile.  It must be initialised using method {@code putStrategy(int,S)}.
	 * In the implementation of this method, the players' payoff is specified by calling
	 * method {@code GamePlayers.setPayoff(int,float)}.

	 * @param strategies The players that are going to play a game.

	 * @see intopo.game.Game.Strategies#setPayoff(int,float)
	 * @see intopo.game.Game.Strategies#putStrategy(int, intopo.game.Strategy)
	 */
	public void play (Strategies<S> strategies);

	/**
	 * Get the lowest payoff obtainable in this game.

	 * @return the lowest payoff that a player can get in this game.
	 */
	public float lowestPayoff ();

	/**
	 * Get the highest payoff that a player can obtain in this game.

	 * @return the highest payoff obtainable in this game.
	 */
	public float highestPayoff ();

	/**
	 * Return a payoff that a player in a Pareto Optimal profile can obtain.

	 * @return a payoff of a player in a Pareto Optimal profile.
	 */
	public float paretoPayoff ();
	/**
	 * Writes this game in the given text stream.  The data must be read from a constructor that accepts a {@code Scanner} instance that encapsulates a text stream.
	 *
	 * <p> The text must begin with a word.  This word is the key in the map ADT that is returned by a game description as specified by method {@code getGameConstructor()} in interface {@code DescriptionText}.
	 * 
	 * @param ps
	 *
	 * @see DescriptionText#getGameConstructor()
	 */
	public void write (PrintStream ps);
	/**
	 * This class is an aggregation of strategies. Strategies in this
	 * collection are referred by an index that represents their position or
	 * role in the game. The meaning of this role depends on the game.

	 * @param <S> The type of strategy's strategies that this aggregation holds.

	 * @author <a href="mailto:plsm@ua.pt">Pedro Lopes da Silva Mariano</a>

	 * @version 3.0
	 */
	static final public class Strategies<S extends Strategy<S>>
	{
		/**
		 * The array that contains the strategies.
		 */
		final private Object[] strategies;

		/**
		 * The array that contains strategies' payoffs obtained after playing a
		 * game.
		 */
		final private float[] payoffs;


		/**
		 * Sole constructor that initialises the arrays {@code strategies} and
		 * {@code payoffs}. A game is used as this aggregation holds exactly the
		 * number of strategies in a game.

		 * @param game The game used to obtain its number of strategies.

		 * @see #strategies
		 * @see #payoffs
		 */
		public Strategies (Game<S> game)
		{
			this (game.numberPlayers ());
		}

		public Strategies (int numberPlayers)
		{
			strategies = new Object [numberPlayers];
			java.util.Arrays.fill (strategies, null);
			payoffs = new float [numberPlayers];
		}

		/**
		 * Initialises the position {@code index} of arrays {@code strategies} and
		 * {@code payoffs}.

		 * @param index The index to initialise.
		 * @param strategy The strategy that is goind to occupy role {@code index}.
		 */
		public void putStrategy (int index, S strategy)
		{
			strategies [index] = strategy;
			payoffs [index] = 0;
		}

		/**
		 * Returns the strategy at the specified index.

		 * @param index The index of the strategy to return.

		 * @return The strategy at the specified index.

		 * @throws IndexOutOfBoundsException If the index is out of bounds.
		 */
		public S getStrategy (int index)
			throws IndexOutOfBoundsException
		{
			return (S) strategies [index];
		}


		/**
		 * Gets the payoff obtainted by the strategy at the given index.

		 * @param index The index of the strategy payoff.

		 * @return The payoff of the strategy to return.
		 */
		public float getPayoff (int index)
		{
			return payoffs [index];
		}

		/**
		 * Sets the payoff obtained the strategy at the given index.

		 * <p> Implementors of interface {@code Game} should call this method in
		 * order to set the payoff obtained by strategies.

		 * <dl><dt><b>Pre-Condition:</b></dt><dd>This method should be called
		 * once per parameter by the implementor of interface {@code
		 * Game}.</dd></dl>

		 * @param index The index of the strategy.
		 * @param payoff The payoff obtained by the specified strategy.

		 * @see Game
		 */
		public void setPayoff (int index, float payoff)
		{
			payoffs [index] = payoff;
		}
		public String toString ()
		{
			StringBuffer result = new StringBuffer ((10 + 40) * this.payoffs.length + 1);
			for (int i = 0; i < this.payoffs.length; i++) {
				result.append (String.format ("%8f  ", this.payoffs [i]));
				result.append (this.strategies [i]);
				result.append ("\n");
			}
			return result.toString ();
		}
	}
}
